#!/usr/bin/perl -w


BEGIN
{
	my $cdir=$0;
	$cdir =~ s/^(.*)\/([^\/]+$)/$1/;
	unshift (@INC, $cdir);
	unshift (@INC, $cdir."../lib");
}

my @base64_alphabet=
('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/','=');

sub base64_trans_byte
{
	my(@s)=@_;
	my @d=('0','0','0','0');
	my ($temp1,$temp2);

	$temp1=ord($s[0])&252;
	$temp1=$temp1>>2;
	$d[0]=$base64_alphabet[$temp1];

	$temp1=ord($s[0])&3;
	$temp1=$temp1<<4;
	$temp2=ord($s[1])&240;
	$temp2=$temp2>>4;
	$d[1]=$base64_alphabet[$temp1+$temp2];

	$temp1=ord($s[1])&15;
	$temp1=$temp1<<2;
	$temp2=ord($s[2])&192;
	$temp2=$temp2>>6;
	$d[2]=$base64_alphabet[$temp1+$temp2];

	$d[3]=$base64_alphabet[ord($s[2])&63];
	return @d;
}

sub base64enc
{
	my (@inlines,$line,@src,@dst,$dstline,$dcount,$count,@buff,$out);
	my ($in)=@_;
	$out="";
	$count=0;
	$dcount=0;
	@inlines=split(/\n/,$in);
	while($#inlines >= 0)
	{
		$line=shift(@inlines);
		@buff=split("",$line);
		foreach my $ch(@buff)
		{
			if($count<3)
			{
				$src[$count]=$ch;
				$count++;
			}
			if($count==3)
			{
				if($dcount==64)
				{
					$out=join("",$out,"\n");
					$dcount=0;
				}
				@dst=&base64_trans_byte(@src);
				$out=join("",$out,@dst);
				$count=0;$dcount+=4;
				$src[$count]=$ch;
			}
		}
	}
	if($count==1)
	{
		$src[1]=chr(00);
		$src[2]=chr(00);
		if($dcount==64) {$out=join("",$out,"\n");$dcount=0;}
		@dst=&base64_trans_byte(@src);
		$dst[2]=$dst[3]='=';
		$out=join("",$out,@dst);
		$count=0;$dcount+=4;
	}
	if($count==2)
	{
		$src[2]=chr(00);
		if($dcount==64) {$out=join("",$out,"\n");$dcount=0;}
		@dst=&base64_trans_byte(@src);
		$dst[3]='=';
		$out=join("",$out,@dst);
		$count=0;$dcount+=4;
	}


	$out=join("",$out,"\n");
	return $out;
}

sub base64enc_file
{
	my ($line,@src,@dst,$dstline,$dcount,$count,@buff);
	my ($infile,$outfile)=@_;
	$count=0;
	$dcount=0;

	open(INFILE,"$infile") or return 1;
	open(OUTFILE,">$outfile") or return 2;
	while($line=<INFILE>)
	{
		@buff=split("",$line);
		foreach my $ch (@buff)
		{
			if($count<3)
			{
				$src[$count]=$ch;
				$count++;
			}
			if($count==3)
			{
				if($dcount==64) {print OUTFILE "\n";$dcount=0;}
				@dst=&base64_trans_byte(@src);
				print OUTFILE @dst;
				$count=0;$dcount+=4;
				$src[$count]=$ch;
			}
		}
	}
	if($count==1)
	{
		$src[1]=chr(00);
		$src[2]=chr(00);
		if($dcount==64) {print OUTFILE "\n";$dcount=0;}
		@dst=&base64_trans_byte(@src);
		$dst[2]=$dst[3]='=';
		print OUTFILE @dst;
		$count=0;$dcount+=4;
	}
	if($count==2)
	{
		$src[2]=chr(00);
		if($dcount==64) {print OUTFILE "\n";$dcount=0;}
		@dst=&base64_trans_byte(@src);
		$dst[3]='=';
		print OUTFILE @dst;
		$count=0;$dcount+=4;
	}
	print OUTFILE "\n";
	close INFILE;
	close OUTFILE;
	return 0;
}

sub usage
{
	print("Usage:\n");
	print("smmail.pl -h\n");
	print("smmail.pl -v\n");
	print("smmail.pl -m Mail_Server -s sender [-x fake sender] [-p password] -r \"receiver1,[receiver2],[receiver3] ...\" [-c \"receiver1,[receiver2],[receiver3] ...\"] [-S Subject] [-a attachments] [-f Mail_Content_File]\n");
	print("options:\n");
	print("-h    show this massage.\n");
	print("-v    show version.\n");
	print("-m    specify you mail server in IP or HOSTNAME(SMTP server).\n");
	print("-s    specify sender (default is your login ID).\n");
	print("-x    specify sender showed in mail header (Fake Sender).***Please Use This Function Cagily***\n");
	print("-p    if server need auth, use this option to provide password.\n");
	print("-S    Subject.\n");
	print("-r    specify receivers, multiple receivers will splited by ','.\n");
	print("-c    specify carbon copy receivers, multiple receivers will splited by ','.\n");
	print("-a    specify attachments, multiple attachments will splited by a space.\n");
	print("-f    specify a file contained mail content in html format;\n");
	print("      if no filename specified or use '-', smmail will read standard input.\n");
	exit 0;
}

sub check_file
{
	my($file_name)=@_;
	if(! -e $file_name)
	{
		print("file: ".$file_name." is not exist.\n");
		exit 1;
	}
	if(! -f $file_name)
	{
		print("file: ".$file_name." is not a regular file.\n");
		exit 2;
	}

	if(! -r $file_name)
	{
		print("file: ".$file_name." is not readable.\n");
		exit 3;
	}
	return 0;
}

sub html_charset
{
	my($htfile)=@_;
	my($htline);
	open(HTML,$htfile) || die ("open $htfile failed");
	while($htline=<HTML>)
	{
		if($htline =~ /charset=/i)
		{
			$htline =~ s/^.*charset=//i;
			$htline =~ s/(^[0-9a-zA-Z-]+).*/$1/;
			chomp($htline);
			return $htline;
		}
	}
	return "utf-8";
}

use vars qw/ %opt /;

use Net::Telnet ();
use Getopt::Std;
my ($rt, $count, $username, $password, $hostname, $htcharset, $line, $bodyfile, $bodyline, $attachmentline, @attachments, $xmailfrom, $mailfrom, @rcptto, @cc, $subject, $smtp);


### get options ###

getopts('hvm:s:S:x:p:r:c:f:a:', \%opt);

if($opt{v})
{
	print("this is a perl script for send a mail contain html format body.\n");
	print("Author: HongYu(tinyhong\@163.com).\n");
	print("Version: 0.1\n");
	exit 0;
}
if($opt{h}) {usage;}
if(!$opt{m} || !$opt{s} || !$opt{r}) {usage;}

while (($key, $value) = each(%opt) ) {
	print "$key = $value\n";
}

###########################
### check & put options ###
###########################


if($opt{f} && $opt{f} ne "-")
{
	&check_file($opt{f});
	$bodyfile=$opt{f};
}
else
{
	$bodyfile="/tmp/smmail_body";
	open (BODYFILE,">/tmp/smmail_body") or die "open $bodyfile faild.\n";
	while(<STDIN>)
	{
		print BODYFILE $_;
	}
	close BODYFILE;
}
$htcharset=&html_charset($bodyfile);
print $bodyfile," charset is ",$htcharset,"\n";

if($opt{a})
{
	$opt{a} =~ s/\t/\ /g;
	$opt{a} =~ s/(\ )+/$1/g;
	@attachments=split(/ /,$opt{a});
	$count=1;
	while($count <= @attachments)
	{
		&check_file($attachments[$count-1]);
		$count++;
	}
}

$hostname = $opt{m};
$mailfrom = $opt{s};

if($opt{p})
{
	$username=$opt{s};
	$password=$opt{p};
	print $username,":",$password;
	print "\n";
	$username=&base64enc($username);
	$password=&base64enc($password);
	chomp($username);
	chomp($password);
	print $username,":",$password;
	print "\n";
}

if($opt{x})
{
	$xmailfrom=$opt{x};
	print "fake sender = ".$xmailfrom."\n";
}
else
{
	$xmailfrom=$mailfrom;
}

@rcptto = split(/,/,$opt{r});
if($opt{c})
{
	@cc=split(/,/,$opt{c});
}
if($opt{S})
{
	$subject = $opt{S};
}
else
{
	$subject = "None";
}

$smtp = new Net::Telnet (Telnetmode => 0);
$smtp->open(Host => $hostname,
Port => 25);


## Read connection message.
$line = $smtp->getline;
print $line;
die $line unless $line =~ /^220/;

## Send hello.
$smtp->print("HELO www.ziling.com");
$line = $smtp->getline;
die $line unless $line =~ /^250.+/;

## auth if need
if($opt{p})
{
	$smtp->print("EHLO www.ziling.com");
	
	$line="";
	while()
	{
		if($line =~ /OK$/)
		{
			last;
		}
		else
		{
			$line = $smtp->getline;
		}
	}
	$smtp->print("auth login");
	$line = $smtp->getline;
	die $line unless $line =~ /^334.+/;

	$smtp->print($username);
	$line = $smtp->getline;
	die $line unless $line =~ /^334.+/;

	$smtp->print($password);
	$line = $smtp->getline;
	die $line unless $line =~ /^235.+/;

	print $line;
}

## mail from
print("mail from:<".$mailfrom.">");
$smtp->print("mail from:<".$mailfrom.">");
$line = $smtp->getline;
print $line;
die $line unless $line =~ /^250.+/;

## rcpt to
$count = 1;
while ($count <= @rcptto)
{
	$smtp->print("rcpt to:<".$rcptto[$count-1].">");
	$line = $smtp->getline;
	print "rcpt to:<".$rcptto[$count-1].">\n".$line;
	die $line unless $line =~ /^250.+/;
	$count++;
}
if($opt{c})
{
	$count = 1;
	while ($count <= @cc)
	{
		$smtp->print("rcpt to:<".$cc[$count-1].">");
		$line = $smtp->getline;
		print "rcpt to:<".$cc[$count-1].">\n".$line;
		die $line unless $line =~ /^250.+/;
		$count++;
	}
}
## body
$smtp->print("data");
$line = $smtp->getline;
die $line unless $line =~ /^354.+/;

$smtp->print("From: ".$xmailfrom);
$count = 1;
while ($count <= @rcptto)
{
	$smtp->print("To: <".$rcptto[$count-1].">");
	$count++;
}
if($opt{c})
{
	$count = 1;
	while ($count <= @cc)
	{
		$smtp->print("Cc: <".$cc[$count-1].">");
		$count++;
	}
}
$smtp->print("Subject: ".$subject);
$smtp->print("Mime-Version: 1.0");
$smtp->print("Content-Type: multipart/mixed; boundary=\"{[(^_^)]}\"");
$smtp->print("\n");
$smtp->print("--{[(^_^)]}");
$smtp->print("Content-Type: text/html; charset=\"".$htcharset."\"");
$smtp->print("Content-Transfer-Encoding: base64");
$smtp->print("\n");
my $encodedfile=$bodyfile;
$encodedfile =~ s/^(.*\/)+([^\/]+$)/$2/;
$encodedfile="/tmp/".$encodedfile.".encoded";

$rt=&base64enc_file($bodyfile,$encodedfile);
if($rt != 0)
{
	print "encode file ".$bodyfile." failed.";
	exit 5;
}

print "sending $bodyfile....";
open(HTFILE,$encodedfile) || die ("open file ".$encodedfile." failed");
while ($bodyline = <HTFILE>)
{
	$smtp->print($bodyline);
}
$smtp->print("\n");
close(HTFILE);
unlink($encodedfile);
print "done.\n";

if($opt{a})
{
	$count = 1;
	while ($count <= @attachments)
	{
		print "sending $attachments[$count-1] ...";

		my $attname=$attachments[$count-1];
		$attname =~ s/^(.*\/)+([^\/]+$)/$2/;

		$attachmentname=$attachments[$count-1];
		$attachmentname =~ s/^(.*\/)+([^\/]+$)/$2/;
	
		$attachmentname="/tmp/".$attachmentname.".encoded";
		print $attachmentname,"\n";
		$rt=&base64enc_file($attachments[$count-1],$attachmentname);
		if($rt != 0)
		{
			print "encode file ".$attachments[$count-1]." failed.";
			next;
		}
		$smtp->print("--{[(^_^)]}");
		$smtp->print("Content-Type: img/jpeg; charset=\"iso-8859-1\"");
		$smtp->print("Content-Transfer-Encoding: base64");
		$smtp->print("Content-Disposition: attachment; filename=\"".$attname."\"");
		$smtp->print("\n");
		open(AFILE,$attachmentname) || die ("open file ".$attachmentname."failed");
		while ($attachmentline = <AFILE>)
		{
			$smtp->print($attachmentline);
		}
		close(AFILE);
		unlink($attachmentname);
		$count++;
		print "done.\n";
	}
}
$smtp->print("--{[(^_^)]}--");
$smtp->print("\n");
$smtp->print(".");
$line = $smtp->getline;
die $line unless $line =~ /^250.+/;
print "sent OK\n";
## quit.
$smtp->print("quit");
$line = $smtp->getline;

exit;

